package cn.net.isir.common;

import cn.net.isir.common.config.WxConfig;
import cn.net.isir.utils.StringUtils;
import org.apache.log4j.Logger;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author liuxubo
 * @filename XML.java
 * @package cn.net.isir.common
 * @email lxb@isir.net.cn
 * @date 2018/09/17
 * @comment
 * @since 1.7
 */
public class XML {

    private static Logger logger = Logger.getLogger(XML.class);

    /**
     * 键
     */
    private String name;
    /**
     * 值
     */
    private String value;
    /**
     * 子节点列表
     */
    private List<XML> children = new ArrayList<>();

    /**
     * 向当前节点的子节点列表中添加子节点
     *
     * @param xml 子节点XML
     * @return 返回父节点
     */
    public XML addChild(XML xml) {
        this.children.add(xml);
        return this;
    }

    /**
     * 向当前节点的子节点列表中添加子节点
     *
     * @param name  键
     * @param value 值
     * @return 返回父节点
     */
    public XML addChild(String name, String value) {
        return this.addChild(generateXML(name, value));
    }

    /**
     * 向当前节点的子节点列表中添加子节点(空节点)
     *
     * @param name 键
     * @return 返回父节点
     */
    public XML addChild(String name) {
        return this.addChild(generateXML(name));
    }

    /**
     * 生成一个节点
     *
     * @param name  键
     * @param value 值
     * @return 返回生成的节点。该节点拥有一个键，一个值，还有一个空的子节点列表
     */
    public static XML generateXML(String name, String value) {
        XML xml = new XML();
        xml.name = name;
        xml.value = value;
        return xml;
    }

    /**
     * 生成一个节点
     *
     * @param name 键
     * @return 返回生成的节点。该节点拥有一个键，一个值(为空字符串)，还有一个空的子节点列表
     */
    public static XML generateXML(String name) {
        return generateXML(name, "");
    }

    /**
     * 将XML类转换为XML字符串
     *
     * @return XML字符串
     */
    public String toString() {
        String str = toStringHandle(this);
        logger.info(str);
        return str;
    }

    private static String toStringHandle(XML xml) {
        StringBuilder sb = new StringBuilder();
        //添加开始标签
        sb.append("<").append(xml.name).append(">");
        //判断子节点数量
        int length = xml.children.size();
        //如果是叶子节点，直接连接值字符串
        if (length == 0) {
            //如果不为空，则添加，为空则不添加(只有开始结束标签，中间无内容)
            if (xml.value != null) {
                sb.append(xml.value);
            }
            if (StringUtils.isBlank(xml.value)) {
                return "";
            }
        }
        //如果不是叶子节点，则获取子节点，递归将子节点转换为XML字符串并返回
        if (length != 0) {
            for (XML subXml : xml.children) {
                sb.append(toStringHandle(subXml));
            }
        }
        //添加关闭标签
        sb.append("</").append(xml.name).append(">");
        return sb.toString();
    }

    /**
     * 将XML类转换为XML字符串
     *
     * @return XML字符串
     */
    public String toConvertString() {
        String str = toConvertStringHandle(this);
        logger.info(str);
        return str;
    }

    private static String toConvertStringHandle(XML xml) {
        StringBuilder sb = new StringBuilder();
        //添加开始标签
        sb.append("<").append(xml.name).append(">");
        //判断子节点数量
        int length = xml.children.size();
        //如果是叶子节点，直接连接值字符串
        if (length == 0) {
            //如果不为空，则添加，为空则不添加(只有开始结束标签，中间无内容)
            if (xml.value != null) {
                sb.append("<![CDATA[");
                sb.append(xml.value);
                sb.append("]]>");
            }
        }
        //如果不是叶子节点，则获取子节点，递归将子节点转换为XML字符串并返回
        if (length != 0) {
            for (XML subXml : xml.children) {
                sb.append(toConvertStringHandle(subXml));
            }
        }
        //添加关闭标签
        sb.append("</").append(xml.name).append(">");
        return sb.toString();
    }

    /**
     * 根据xml进行ASCII排序，排序后，连接并使用MD5加密(微信支付专用)
     *
     * @return 签名字符串
     */
    public String sign() {
        StringBuffer sb = new StringBuffer();
        List<XML> sx = new ArrayList<>();

        SortedMap<String, String> smap = new TreeMap<>();
        for (XML xml : children) {
            if (xml.value != null) {
                smap.put(xml.name, xml.value);
            }
        }
        Set<String> keys = smap.keySet();
        for (String key : keys) {
            sb.append(key).append("=").append(smap.get(key)).append("&");
        }
        logger.info(sb.toString());

        sb = sb.append("key=").append(WxConfig.getMch_secret());
        logger.info(sb.toString());
        String sign = StringUtils.MD5(sb.toString()).toUpperCase();
        logger.info(this.toString());
        logger.info(sign);
        return sign;
    }

    public static List<XML> parse(String xmlStr) {

        while (xmlStr.length() > 0) {
            String firstTagName = xmlStr.substring(xmlStr.indexOf("<") + 1, xmlStr.indexOf(">"));
            int firstTagEndIndex = xmlStr.indexOf("</" + firstTagName + ">");

            Pattern regex = Pattern.compile("<[^</]+>[^<]*</[^<]+>");

            XML xml;
            Matcher m = regex.matcher(xmlStr);
            String tmp = null;
            if (m.find()) {
                tmp = m.group();
                xml = parseOne(m.group());
            }
            if (tmp != null) {
                xmlStr = xmlStr.substring(0, xmlStr.indexOf(tmp)) + xmlStr.substring(xmlStr.indexOf(tmp), xmlStr.length());
            }

        }
        return null;
    }

    public static XML parseOne(String xml) {
        String name = xml.substring(xml.indexOf("<"), xml.indexOf(">"));
        String value = xml.substring(xml.indexOf(">"), xml.lastIndexOf("</"));
        return generateXML(name, value);
    }


    public static void main(String[] args) {
        XML root = XML.generateXML("XML", null);
        root.addChild("name", "lxb").addChild("age", "18").addChild("sex", "man");
        root.addChild("name", "lxb").addChild("age", "18").addChild("sex", "man");
        root.toString();
        root.toConvertString();
        XML xml = XML.generateXML("root");
        xml.addChild(root);
        xml.addChild(root);
        xml.addChild("ca");
        logger.info(xml.toString());
//        XML.parse(xml.toString());
    }

    public static void main2(String[] args) {
        XML xml = XML.generateXML("xml");
        xml.addChild("return_code", "SUCCESS");
        xml.addChild("return_msg", "SUCCESS");
        logger.info(xml.toConvertString());
    }

}
